﻿// File operation utilities for UTF-8 encoding support
#include "stdafx.h"
#include "util.h"
#include <filesystem>
#include <iostream>
#include <fstream>
#include <io.h>


#ifndef OS_WIN
#error "Only used on MS-Windows"
#endif

using namespace std;

namespace shared{
    namespace filepath {
        static const char kEndChar = '\0';
        static const char kFilePathSeparators[] = "\\/";
        static const char kFilePathCurrentDirectory[] = ".";
        static const char kFilePathParentDirectory[] = "..";
        static const char kFilePathExtensionSeparator = '.';

        std::wstring GetAppDir()
        {
            TCHAR chFileName[MAX_PATH + 1] = { 0 };
            ::GetModuleFileName(GetModuleHandle(NULL), chFileName, MAX_PATH);
            wstring sInstancePath = chFileName;
            int pos = sInstancePath.rfind(_T('\\'));
            if (pos >= 0)
                sInstancePath = sInstancePath.substr(0, pos + 1);
            else
                sInstancePath = L"";
            return sInstancePath;
        }

        std::wstring GetAppDataLocalDir() {
            return nbase::win32::GetLocalAppDataDir();
        }

        template<class _Ty>
        bool IsFileExisted(_Ty sFile)
        {
#if 1
            if  constexpr (std::is_same<_Ty, std::wstring>()) {
                return _waccess(sFile.c_str(), 0) == 0;
            }
            else if constexpr (std::is_same<_Ty, std::string>())
                return access(sFile.c_str(), 0) == 0;
            else
                return false;
#else
            return std::filesystem::exists(sFile);
#endif
        }
        
        template<class _Ty>
        bool IsDirExisted(_Ty sDir)
        {
            if  constexpr (std::is_same<_Ty, std::wstring>()) {
                return _waccess(sDir.c_str(), 0) == 0;
            }
            else if constexpr (std::is_same<_Ty, std::string>())
                return access(sDir.c_str(), 0) == 0;
            else
                return false;
        }

        bool IsFilePathSeparator(const char separator)
        {
            if (separator == kEndChar)
                return false;

            size_t len = sizeof(kFilePathSeparators) / sizeof(char);
            for (size_t i = 0; i < len; i++)
            {
                if (separator == kFilePathSeparators[i])
                    return true;
            }

            return false;
        }

        bool FilePathExtension(const string& filepath_in, string& extension_out)
        {
            if (filepath_in.size() == 0)
                return false;
            size_t pos = filepath_in.rfind(kFilePathExtensionSeparator);
            if (pos == string::npos)
                return false;
            extension_out = filepath_in.substr(pos, string::npos);
            return true;
        }

        bool FilePathApartDirectory(const string& filepath_in, string& directory_out)
        {
            size_t index = filepath_in.size() - 1;
            if (index <= 0 || filepath_in.size() == 0)
                return false;
            for (; index != 0; index--)
            {
                if (IsFilePathSeparator(filepath_in[index]))
                {
                    if (index == filepath_in.size() - 1)
                        directory_out = filepath_in;
                    else
                        directory_out = filepath_in.substr(0, index + 1);
                    return true;
                }
            }
            return false;
        }

        // Get the filename from the whole filepath
        bool FilePathApartFileName(const string& filepath_in, string& filename_out)
        {
            size_t index = filepath_in.size() - 1;
            if (index <= 0 || filepath_in.size() == 0)
                return false;
            for (; index != 0; index--)
            {
                if (IsFilePathSeparator(filepath_in[index]))
                {
                    if (index == filepath_in.size() - 1)
                        return false;
                    filename_out = filepath_in.substr(index + 1, string::npos);
                    return true;
                }
            }
            return false;
        }

        template<typename CharType>
        bool ParsePathComponentsT(const CharType* path, const CharType* seperators, std::list<std::basic_string<CharType> >& components)
        {
            components.clear();
            if (path == NULL)
                return false;
            const CharType* prev = NULL;
            const CharType* next = path;
            const CharType* c;
            while (*next)
            {
                prev = next;
                // find the first seperator
                for (;;)
                {
                    for (c = seperators; *c && *next != *c; c++);
                    if (*c || !*next)
                        break;
                    next++;
                }
                components.push_back(std::basic_string<CharType>(prev, next - prev));
                if (*next)
                    components.back().push_back(*seperators);
                // skip duplicated seperators
                for (++next;;)
                {
                    for (c = seperators; *c && *next != *c; c++);
                    if (!*c)
                        break;
                    next++;
                }
            }
            return true;
        }

        bool ParsePathComponents(const char* path, std::list<string>& components)
        {
            return ParsePathComponentsT<char>(path, kFilePathSeparators, components);
        }

        bool IsDirectoryComponent(const string& component)
        {
            if (component.empty())
                return false;
            return *component.rbegin() == kFilePathSeparators[0] || *component.rbegin() == kFilePathSeparators[1];
        }

        bool FilePathCompose(const string& directory_in, const string& filename_in, string& filepath_out)
        {
            string directory;
            if (!FilePathApartDirectory(directory_in, directory))
                return false;
            filepath_out = directory + filename_in;
            return true;
        }


        void TemplateCompile() {
            std::string value;
            std::wstring sValue;
            IsFileExisted(value);
            IsFileExisted(sValue);
            IsDirExisted(value);
            IsDirExisted(sValue);
        };
    }
} // namespace shared
